<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.2/css/all.min.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"littlefxc.github.io","root":"/","images":"/images","scheme":"Mist","version":"8.2.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}};
  </script>
<meta name="description" content="本文介绍如何结合 Kubernetes + Jenkis + SVN + Harbor + Helm 实现一个完整的 CI&#x2F;CD 流水线作业进行持续集成和持续部署。">
<meta property="og:type" content="article">
<meta property="og:title" content="基于Jenkins、SVN、Harbor、Helm和Kubernetes的持续集成">
<meta property="og:url" content="http://littlefxc.github.io/2020/04/28/%E5%9F%BA%E4%BA%8EJenkins%E3%80%81SVN%E3%80%81Harbor%E3%80%81Helm%E5%92%8CKubernetes%E7%9A%84%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/index.html">
<meta property="og:site_name" content="一年春又来">
<meta property="og:description" content="本文介绍如何结合 Kubernetes + Jenkis + SVN + Harbor + Helm 实现一个完整的 CI&#x2F;CD 流水线作业进行持续集成和持续部署。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://gitee.com/littlefxc/oss/raw/master/images/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70.png">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200509194819239.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200509194819236.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200509194819215.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200509200524318.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200509194818424.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200509194818496.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70">
<meta property="article:published_time" content="2020-04-28T05:59:44.000Z">
<meta property="article:modified_time" content="2021-08-30T05:40:21.471Z">
<meta property="article:author" content="一年春又来">
<meta property="article:tag" content="k8s">
<meta property="article:tag" content="helm">
<meta property="article:tag" content="jenkins">
<meta property="article:tag" content="CI&#x2F;CD">
<meta property="article:tag" content="svn">
<meta property="article:tag" content="devops">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://gitee.com/littlefxc/oss/raw/master/images/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70.png">


<link rel="canonical" href="http://littlefxc.github.io/2020/04/28/%E5%9F%BA%E4%BA%8EJenkins%E3%80%81SVN%E3%80%81Harbor%E3%80%81Helm%E5%92%8CKubernetes%E7%9A%84%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>
<title>基于Jenkins、SVN、Harbor、Helm和Kubernetes的持续集成 | 一年春又来</title>
  




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="一年春又来" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">一年春又来</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu">
        <li class="menu-item menu-item-home"><a href="/" rel="section"><i class="home                          //首页 fa-fw"></i>首页</a></li>
        <li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="archive          //归档 fa-fw"></i>归档</a></li>
        <li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="th           //分类 fa-fw"></i>分类</a></li>
        <li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="tags                     //标签 fa-fw"></i>标签</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1-%E5%AE%89%E8%A3%85-Helm"><span class="nav-text">1 安装 Helm</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2-%E4%BD%BF%E7%94%A8-Kubernetes-%E7%9A%84%E5%8A%A8%E6%80%81%E5%AD%98%E5%82%A8"><span class="nav-text">2 使用 Kubernetes 的动态存储</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#2-1-CentOS-7-%E4%B8%8B-yum-%E5%AE%89%E8%A3%85%E5%92%8C%E9%85%8D%E7%BD%AE-NFS"><span class="nav-text">2.1 CentOS 7 下 yum 安装和配置 NFS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-2-%E5%AE%89%E8%A3%85-nfs-client-provisioner"><span class="nav-text">2.2 安装 nfs-client-provisioner</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#3-k8s-%E4%B8%AD%E5%AE%89%E8%A3%85-Jenkins"><span class="nav-text">3 k8s 中安装 Jenkins</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#4-k8s-%E4%B8%AD%E5%AE%89%E8%A3%85-Redis-%E9%9B%86%E7%BE%A4"><span class="nav-text">4 k8s 中安装 Redis 集群</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#5-k8s-%E4%B8%AD%E9%85%8D%E7%BD%AE%E5%A4%96%E9%83%A8%E7%9A%84-mysql"><span class="nav-text">5 k8s 中配置外部的 mysql</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#6-%E6%8C%81%E7%BB%AD%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B"><span class="nav-text">6 持续构建流程</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#7-Docker-%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA"><span class="nav-text">7 Docker 镜像构建</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#8-Jenkins"><span class="nav-text">8 Jenkins</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#8-1-%E9%85%8D%E7%BD%AE"><span class="nav-text">8.1 配置</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-2-%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%B5%81%E6%B0%B4%E7%BA%BF%E4%BB%BB%E5%8A%A1"><span class="nav-text">8.2 创建一个流水线任务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-3-%E5%88%9B%E5%BB%BA3%E4%B8%AA%E5%8F%98%E9%87%8F"><span class="nav-text">8.3 创建3个变量</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-4-%E9%85%8D%E7%BD%AE-Pipeline"><span class="nav-text">8.4 配置 Pipeline</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-5-Jenkinsfile-%E7%A4%BA%E4%BE%8B"><span class="nav-text">8.5 Jenkinsfile 示例</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-6-%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AE%E8%87%AA%E5%AE%9A%E4%B9%89SpringBoot%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0"><span class="nav-text">8.6 如何配置自定义SpringBoot应用启动参数</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%8F%82%E8%80%83%E8%B5%84%E6%BA%90"><span class="nav-text">参考资源</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">一年春又来</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">234</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">38</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">125</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://littlefxc.github.io/2020/04/28/%E5%9F%BA%E4%BA%8EJenkins%E3%80%81SVN%E3%80%81Harbor%E3%80%81Helm%E5%92%8CKubernetes%E7%9A%84%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="一年春又来">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="一年春又来">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          基于Jenkins、SVN、Harbor、Helm和Kubernetes的持续集成
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-04-28 13:59:44" itemprop="dateCreated datePublished" datetime="2020-04-28T13:59:44+08:00">2020-04-28</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2021-08-30 13:40:21" itemprop="dateModified" datetime="2021-08-30T13:40:21+08:00">2021-08-30</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/devops/" itemprop="url" rel="index"><span itemprop="name">devops</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <p>本文介绍如何结合 Kubernetes + Jenkis + SVN + Harbor + Helm 实现一个完整的 CI/CD 流水线作业进行持续集成和持续部署。</p>
<span id="more"></span>

<h1 id="1-安装-Helm"><a href="#1-安装-Helm" class="headerlink" title="1 安装 Helm"></a>1 安装 Helm</h1><p>下载 Helm3，同时添加 chart 库</p>
<p><a target="_blank" rel="noopener" href="https://get.helm.sh/helm-v3.1.2-linux-amd64.tar.gz">下载地址</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ tar -zxvf helm-v3.1.2-linux-amd64.tar.gz</span><br><span class="line">$ ln -s /home/k8s-projects/helm-projects/linux-amd64/helm /usr/<span class="built_in">local</span>/bin/helm</span><br><span class="line">$ helm repo add stable http://mirror.azure.cn/kubernetes/charts</span><br><span class="line">$ helm repo add bitnami https://charts.bitnami.com/bitnami</span><br></pre></td></tr></table></figure>

<h1 id="2-使用-Kubernetes-的动态存储"><a href="#2-使用-Kubernetes-的动态存储" class="headerlink" title="2 使用 Kubernetes 的动态存储"></a>2 使用 Kubernetes 的动态存储</h1><p>尽管通过 hostPath 或者 emptyDir 的方式可以用来来持久化我们的数据，但是显然我们还需要更加可靠的存储来保存应用的持久化数据，这样容器在重建后，依然可以使用之前的数据。但是显然存储资源和 CPU 资源以及内存资源有很大不同，为了屏蔽底层的技术实现细节，让用户更加方便的使用，Kubernetes 便引入了 PV 和 PVC 两个重要的资源对象来实现对存储的管理。</p>
<p>本文中我们使用 StorageClass，要使用它就得安装对应的自动配置程序，比如我们这里存储后端使用的是 nfs，那么我们就需要使用到一个 nfs-client 的自动配置程序，我们也叫它 Provisioner，这个程序使用我们已经配置好的 nfs 服务器，来自动创建持久卷，也就是自动帮我们创建 PV。</p>
<h2 id="2-1-CentOS-7-下-yum-安装和配置-NFS"><a href="#2-1-CentOS-7-下-yum-安装和配置-NFS" class="headerlink" title="2.1 CentOS 7 下 yum 安装和配置 NFS"></a>2.1 <strong>CentOS 7 下 yum 安装和配置 NFS</strong></h2><a href="/2020/04/29/CentOS7%E4%B8%8Byum%E5%AE%89%E8%A3%85%E5%92%8C%E9%85%8D%E7%BD%AENFS/" title="CentOS7下yum安装和配置NFS">CentOS7下yum安装和配置NFS</a>

<h2 id="2-2-安装-nfs-client-provisioner"><a href="#2-2-安装-nfs-client-provisioner" class="headerlink" title="2.2 安装 nfs-client-provisioner"></a>2.2 安装 nfs-client-provisioner</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ helm install nfs-client-provisioner-dev stable/nfs-client-provisioner --<span class="built_in">set</span> nfs.server=192.168.200.19 \</span><br><span class="line">--<span class="built_in">set</span> nfs.path=/home/k8s-projects/k8s-nfs --<span class="built_in">set</span> storageClass.defaultClass=<span class="literal">true</span></span><br></pre></td></tr></table></figure>

<p>查看安装结果：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ kubectl get storageClass -n dev</span><br><span class="line">NAME                   PROVISIONER                                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE</span><br><span class="line">nfs-client (default)   cluster.local/nfs-client-provisioner-dev   Delete          Immediate           <span class="literal">true</span>                   13d</span><br></pre></td></tr></table></figure>

<p>可以看到 <code>storageClass</code> 顺利创建。</p>
<h1 id="3-k8s-中安装-Jenkins"><a href="#3-k8s-中安装-Jenkins" class="headerlink" title="3 k8s 中安装 Jenkins"></a>3 k8s 中安装 Jenkins</h1><p>使用 Helm 安装 jenkins，命名空间为 <code>dev</code>，指定存储 <code>storageClass</code> 为 <code>nfs-client</code> </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ helm pull stable/jenkins</span><br><span class="line">$ helm install jenkins-dev /home/k8s-projects/helm-projects/jenkins --namespace dev \</span><br><span class="line">--<span class="built_in">set</span> master.serviceType=NodePort --<span class="built_in">set</span> persistence.storageClass=nfs-client --<span class="built_in">set</span> master.adminPassword=awifi@123</span><br></pre></td></tr></table></figure>

<p>我们利用 Kubernetes 来动态运行 Jenkins 的 Slave 节点，可以很好的来解决传统的 Jenkins Slave 浪费大量资源的缺点。</p>
<h1 id="4-k8s-中安装-Redis-集群"><a href="#4-k8s-中安装-Redis-集群" class="headerlink" title="4 k8s 中安装 Redis 集群"></a>4 k8s 中安装 Redis 集群</h1><p>使用 Helm 安装 Redis 集群， 命名空间为 <code>dev</code>, 指定存储<code>storageClass</code>为<code>nfs-client</code>, 密码设置为<code>123456</code></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ helm pull redis-cluster-dev bitnami/redis-cluster</span><br><span class="line">$ helm install redis-cluster-dev /home/k8s-projects/helm-projects/redis-cluster --namespace=dev \</span><br><span class="line">--<span class="built_in">set</span> global.storageClass=nfs-client,global.redis.password=123456</span><br></pre></td></tr></table></figure>

<h1 id="5-k8s-中配置外部的-mysql"><a href="#5-k8s-中配置外部的-mysql" class="headerlink" title="5 k8s 中配置外部的 mysql"></a>5 k8s 中配置外部的 mysql</h1><p>一般来说，k8s 集群外部的服务，集群内是无法访问的。</p>
<p>以 mysql 为例，我们想要部署的服务中想使用现有的 mysql 数据库，k8s也有解决办法，将外部服务作为 <code>Endpoints</code>，同时以 <code>Service</code> 的形式暴露到集群内。</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Endpoints</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">external-service-mysql</span></span><br><span class="line"><span class="attr">subsets:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">addresses:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">ip:</span> <span class="number">192.168</span><span class="number">.212</span><span class="number">.59</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">port:</span> <span class="number">3306</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Service</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">external-service-mysql</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">ports:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">port:</span> <span class="number">3306</span></span><br></pre></td></tr></table></figure>

<h1 id="6-持续构建流程"><a href="#6-持续构建流程" class="headerlink" title="6 持续构建流程"></a>6 持续构建流程</h1><p><img src="https://gitee.com/littlefxc/oss/raw/master/images/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70.png" alt="在这里插入图片描述"></p>
<ol>
<li>开发人员提交代码到 SVN 代码仓库</li>
<li>Jenkins 触发构建构建任务，根据 Pipeline 脚本定义分步骤构建</li>
<li>先进行代码检出，单元测试</li>
<li>然后进行 Maven 构建（Java 项目）</li>
<li>根据构建结果构建 Docker 镜像</li>
<li>推送 Docker 镜像到 Harbor 仓库</li>
<li>触发更新服务阶段，使用 Helm 安装/更新 Release</li>
<li>查看服务是否更新成功。</li>
</ol>
<h1 id="7-Docker-镜像构建"><a href="#7-Docker-镜像构建" class="headerlink" title="7 Docker 镜像构建"></a>7 Docker 镜像构建</h1><p>以用户数据服务为例。</p>
<p>由于我们要将项目部署到 <code>Kubernetes</code> 集群中去，所以我们需要将服务端进行容器化，所以我们在项目根目录下面添加一个 <code>Dockerfile</code> 文件进行镜像构建：</p>
<figure class="highlight docker"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">8</span>-jdk-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">MAINTAINER</span> fengxuechao &lt;fengxuechao.littlefxc@gmail.com&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> LANG=en_US.UTF-<span class="number">8</span> LANGUAGE=en_US:en LC_ALL=en_US.UTF-<span class="number">8</span> TZ=Asia/Shanghai</span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> mkdir /app /app/config /app/logs</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> ./*.jar /app/app.jar</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8080</span></span><br></pre></td></tr></table></figure>

<p>由于服务端代码是基于Spring Boot构建的，所以我们这里使用一个<code>openjdk</code>的基础镜像，将打包过后的jar包放入镜像之中，然后通过启动容器使用<code>java -jar</code>命令直接启动即可，这里就会存在一个问题了，我们是在 Jenkins 的 Pipeline 中去进行镜像构建的，这个时候项目中并没有打包好的jar包文件，那么我们应该如何获取打包好的jar包文件呢？这里我们可以使用两种方法：</p>
<p>第一种就是如果你用于镜像打包的 Docker 版本大于<code>17.06</code>版本的话，那么你可以使用 Docker 的多阶段构建功能来完成镜像的打包过程，我们只需要将上面的Dockerfile文件稍微更改下即可，将使用maven进行构建的工作放到同一个文件中：</p>
<figure class="highlight docker"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> maven:<span class="number">3.6</span>-alpine as BUILD</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拷贝源码</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> src /usr/app/src</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> pom.xml /usr/app</span></span><br><span class="line"><span class="comment"># 拷贝自定义 maven settings.xml</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> settings.xml /usr/share/maven/ref/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> mvn -f /usr/app/pom.xml clean package -Dmaven.test.skip=<span class="literal">true</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">8</span>-jdk-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">MAINTAINER</span> fengxuechao &lt;fengxuechao.littlefxc@gmail.com&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> LANG=en_US.UTF-<span class="number">8</span> LANGUAGE=en_US:en LC_ALL=en_US.UTF-<span class="number">8</span> TZ=Asia/Shanghai</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> mkdir /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> --from=BUILD /usr/app/target/*.jar /app/app.jar</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8080</span></span><br></pre></td></tr></table></figure>

<p>这里我们定义了两个阶段，第一个阶段利用<code>maven:3.6-alpine</code>这个基础镜像将我们的项目进行打包，然后将该阶段打包生成的jar包文件复制到第二阶段进行最后的镜像打包，这样就可以很好的完成我们的 Docker 镜像的构建工作。</p>
<p>第二种方式就是我们传统的方式，在 Jenkins Pipeline 中添加一个maven构建的阶段，然后在第二个 Docker 构建的阶段就可以直接获取到前面的jar包了，也可以很方便的完成镜像的构建工作，为了更加清楚的说明 Jenkins Pipeline 的用法，我们这里采用这种方式，所以 Dockerfile 文件还是使用第一个就行。</p>
<h1 id="8-Jenkins"><a href="#8-Jenkins" class="headerlink" title="8 Jenkins"></a>8 Jenkins</h1><h2 id="8-1-配置"><a href="#8-1-配置" class="headerlink" title="8.1 配置"></a>8.1 配置</h2><p>首先，我们需要去 Jenkins -&gt; Manage Jenkins -&gt; Manage Plugins, 检查插件 <code>Kubernetes</code>, <code>Subversion</code>有没有安装，没有就安装一下，这两个插件是后面一系列操作的基础。</p>
<p>现在项目准备好了，接下来我们可以开始 Jenkins 的配置，我们想要完全自由灵活的配置我们的Jenkins Slave 节点，我们直接直接在 Pipeline 中去自定义 Slave Pod 中所需要用到的容器模板，这样我们需要什么镜像只需要在 Slave Pod Template 中声明即可，完全不需要去定义一个庞大的 Slave 镜像了。</p>
<p>首先去掉 Jenkins 中 kubernetes 插件中的 Pod Template 的定义，Jenkins -&gt; Manage Jenkins -&gt; Manage Nodes and Clouds -&gt; Configure Clouds -&gt; Kubernetes区域，删除下方的<code>Kubernetes Pod Template</code> -&gt; 保存。</p>
<p><img src="https://img-blog.csdnimg.cn/20200509194819239.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<p>点击按钮”连接测试”。如果出现错误，八成是权限问题，建议你去查看一下官网资料<a target="_blank" rel="noopener" href="https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/">使用 RBAC 鉴权</a>。</p>
<h2 id="8-2-创建一个流水线任务"><a href="#8-2-创建一个流水线任务" class="headerlink" title="8.2 创建一个流水线任务"></a>8.2 创建一个流水线任务</h2><p><img src="https://img-blog.csdnimg.cn/20200509194819236.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h2 id="8-3-创建3个变量"><a href="#8-3-创建3个变量" class="headerlink" title="8.3 创建3个变量"></a>8.3 创建3个变量</h2><p>然后选中 “This project is parameterized”, 添加 3 个 “Boolean Parameter”. 分别是 <code>helmDelete</code>, <code>helmInstall</code> 和 <code>helmUpgrade</code>, 其中默认选中 <code>helmUpgrade</code>.</p>
<p>这 3 个变量与我们自定义的 Jenkinsfile 脚本有关，是必须要创建的。</p>
<p><img src="https://img-blog.csdnimg.cn/20200509194819215.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h2 id="8-4-配置-Pipeline"><a href="#8-4-配置-Pipeline" class="headerlink" title="8.4 配置 Pipeline"></a>8.4 配置 Pipeline</h2><p>我们的 Jenkins 的任务是有 Jenkinsfile 推动的.</p>
<p>然后在下面的流水线区域我们可以选择<code>Pipeline script</code>然后在下面测试流水线脚本，我们这里选择<code>Pipeline script from SCM</code>，意思就是从代码仓库中通过<code>Jenkinsfile</code>文件获取Pipeline script脚本定义，然后选择 SCM 来源为 Subversion(如果没有，先去安装这个插件)，在出现的列表中配置上仓库地址 http://…/docker，由于我们是在一个 Slave Pod 中去进行构建，所以如果使用 SSH 的方式去访问 Gitlab 代码仓库的话就需要频繁的去更新 SSH-KEY，所以我们这里采用直接使用用户名和密码的形式来方式：</p>
<p><img src="https://img-blog.csdnimg.cn/20200509200524318.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>在<code>Credentials</code>区域点击添加按钮添加我们访问 SVN 的用户名和密码：</p>
<p><img src="https://img-blog.csdnimg.cn/20200509194818424.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<p>配置成功后我们只需要往 SVN 仓库推送代码,然后手动点击触发 Pipeline 构建了。接下来我们直接在服务端代码仓库目录下面添加Jenkinsfile文件，用于描述流水线构建流程。</p>
<h2 id="8-5-Jenkinsfile-示例"><a href="#8-5-Jenkinsfile-示例" class="headerlink" title="8.5 Jenkinsfile 示例"></a>8.5 Jenkinsfile 示例</h2><p>首先要说明的是 Jenkinsfile 是基于 Groovy 语言的，与Java有很多的相似性。</p>
<p><code>def</code> 是在Groovy用来定义标识符的关键字.在使用它的时候请注意 <code>def</code> 声明的变量所对应的是否符合你要编译和部署的对象。</p>
<ul>
<li><code>label</code>标签的定义，我们这里使用 UUID 生成一个随机的字符串，这样可以让 Slave Pod 每次的名称都不一样，而且这样就不会被固定在一个 Pod 上面了，以后有多个构建任务的时候就不会存在等待的情况了。</li>
<li><code>dockerRegistryUrl</code> 中是公司docker镜像私服地址。</li>
<li><code>mavenProjects</code> 是项目的根目录</li>
<li><code>svnRemote</code> 是项目的SVN地址</li>
<li><code>appName</code> 是maven编译后的jar包名，和<code>imageTag</code> 一起定位 jar 在maven本地仓库中的地址、docker镜像名组成部分，同时也是helm中的项目名</li>
<li><code>imageTag</code> 是maven编译后的jar包版本，和<code>appName</code> 一起定位 jar 在maven本地仓库中的地址、docker镜像名组成部分</li>
<li><code>namespace</code> 是docker镜像在公司私服harbor中的项目名，可以通过 <a target="_blank" rel="noopener" href="http://alpha-harbor.51iwifi.com/harbor/projects">http://alpha-harbor.51iwifi.com/harbor/projects</a> 查看项目名是否存在。</li>
<li><code>image</code> 是docker镜像编译打包后的镜像名，由 <code>appName</code> 和<code>imageTag</code> 组成</li>
<li><code>mvnBuildModules</code> 是maven编译的模块，格式为 groupId:artifactId。请确保你要编译的maven工程包含在该模块下。</li>
<li><code>chartDir</code> 通用 spring-cloud 应用 k8s helm 部署模板, 你也可以自定义！</li>
<li><code>svnHelm</code> 通用 spring-cloud 应用 k8s helm 部署模板的远程仓库地址</li>
<li><code>dockerPath</code> 是源码检出后在Jenkins Slave Pod 中拥有Jenkinsfile的相对地址，docker镜像编译的工作目录。</li>
<li><code>jenkinsSVNCredentialsId</code> 是你在本文上一章节中定义的Jenkins中的svn凭据，请确保这个凭据定义的用户拥有访问 <code>svnRemote</code> 和 <code>svnHelm</code> 这两个变量定义的远程仓库的权限。</li>
</ul>
<p>我们使用<code>podTemplate</code>来定义不同阶段使用的的容器，阶段总共分为5个阶段：</p>
<p>代码检出 → 单元测试 → Maven 打包 → Docker 镜像构建/推送 → Helm 安装/更新服务。</p>
<p>代码检出在默认的 Slave 容器中即可；单元测试我们这里输出一些期望目标后直接忽略，有需要这个阶段的同学自己添加上自己的测试即可；Maven 打包肯定就需要 maven 的容器了；Docker 镜像构建/推送需要 Docker 环境；最后的 Helm 更新服务是不是就需要一个有 Helm 的容器环境了，所以我们这里就可以很简单的定义podTemplate了，如下定义：</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> label = <span class="string">&quot;jenkins-slave-$&#123;UUID.randomUUID().toString()&#125;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> dockerRegistryUrl = <span class="string">&quot;192.168.195.2&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 项目的根目录</span></span><br><span class="line"><span class="keyword">def</span> mavenProjects = <span class="string">&quot;open-platform-dataservice&quot;</span>;</span><br><span class="line"><span class="keyword">def</span> svnRemote = <span class="string">&quot;http://svn.51iwifi.com/repos/AWIFI-PROJECT/capacitygroup/code/trunk/$&#123;mavenProjects&#125;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 请填写maven编译后的jar包名</span></span><br><span class="line"><span class="keyword">def</span> appName = <span class="string">&quot;opf-dataservice-user-provider&quot;</span>;</span><br><span class="line"><span class="comment">// 请填写maven编译后的jar包版本</span></span><br><span class="line"><span class="keyword">def</span> imageTag = <span class="string">&quot;1.0.10&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> namespace = <span class="string">&quot;dev&quot;</span>;</span><br><span class="line"><span class="keyword">def</span> image = <span class="string">&quot;$&#123;namespace&#125;/$&#123;appName&#125;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 请填写要maven编译的模块，格式为 groupId:artifactId</span></span><br><span class="line"><span class="keyword">def</span> mvnBuildModules = <span class="string">&quot;com.awifi.capacity:open-platform-dataservice-user&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 请选择在配置在Jenkins 中的 SVN 凭据</span></span><br><span class="line"><span class="keyword">def</span> jenkinsSVNCredentialsId = <span class="string">&quot;SVN-fengxc&quot;</span>;</span><br><span class="line"><span class="comment">// 通用 spring-cloud 应用 k8s helm 部署模板, 你也可以自定义！</span></span><br><span class="line"><span class="keyword">def</span> chartDir = <span class="string">&quot;helm-spring-cloud&quot;</span></span><br><span class="line"><span class="keyword">def</span> svnHelm = <span class="string">&quot;http://svn.51iwifi.com/repos/AWIFI-PROJECT/capacitygroup/code/trunk/$&#123;chartDir&#125;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 请填写包含 Jenkinsfile,Dockerfile 的相对路径</span></span><br><span class="line"><span class="keyword">def</span> dockerPath = <span class="string">&quot;$&#123;mavenProjects&#125;/open-platform-dataservice-user/open-platform-dataservice-user-provider/docker&quot;</span>;</span><br><span class="line"></span><br><span class="line">podTemplate(<span class="attr">label:</span> label, <span class="attr">containers:</span> [</span><br><span class="line">        containerTemplate(<span class="attr">name:</span> <span class="string">&#x27;maven&#x27;</span>, <span class="attr">image:</span> <span class="string">&#x27;maven:3.6-alpine&#x27;</span>, <span class="attr">command:</span> <span class="string">&#x27;cat&#x27;</span>, <span class="attr">ttyEnabled:</span> <span class="literal">true</span>),</span><br><span class="line">        containerTemplate(<span class="attr">name:</span> <span class="string">&#x27;docker&#x27;</span>, <span class="attr">image:</span> <span class="string">&#x27;docker&#x27;</span>, <span class="attr">command:</span> <span class="string">&#x27;cat&#x27;</span>, <span class="attr">ttyEnabled:</span> <span class="literal">true</span>),</span><br><span class="line">        containerTemplate(<span class="attr">name:</span> <span class="string">&#x27;helm-kubectl&#x27;</span>, <span class="attr">image:</span> <span class="string">&#x27;dtzar/helm-kubectl:3.1.2&#x27;</span>, <span class="attr">command:</span> <span class="string">&#x27;cat&#x27;</span>, <span class="attr">ttyEnabled:</span> <span class="literal">true</span>)</span><br><span class="line">], <span class="attr">volumes:</span> [</span><br><span class="line">        nfsVolume(<span class="attr">mountPath:</span> <span class="string">&#x27;/root/.m2&#x27;</span>, <span class="attr">readOnly:</span> <span class="literal">false</span>, <span class="attr">serverAddress:</span> <span class="string">&#x27;192.168.200.19&#x27;</span>, <span class="attr">serverPath:</span> <span class="string">&#x27;/home/k8s-projects/k8s-nfs/m2&#x27;</span>),</span><br><span class="line">        hostPathVolume(<span class="attr">mountPath:</span> <span class="string">&#x27;/home/jenkins/.kube&#x27;</span>, <span class="attr">hostPath:</span> <span class="string">&#x27;/root/.kube&#x27;</span>),</span><br><span class="line">        hostPathVolume(<span class="attr">mountPath:</span> <span class="string">&#x27;/var/run/docker.sock&#x27;</span>, <span class="attr">hostPath:</span> <span class="string">&#x27;/var/run/docker.sock&#x27;</span>)</span><br><span class="line">]) &#123;</span><br><span class="line">    node(label) &#123;</span><br><span class="line">       stage(<span class="string">&#x27;代码检出&#x27;</span>) &#123;</span><br><span class="line">            echo <span class="string">&quot;1. 代码检出阶段&quot;</span>;</span><br><span class="line">            sh <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">               rm -fr ./*</span></span><br><span class="line"><span class="string">               mkdir $&#123;mavenProjects&#125;</span></span><br><span class="line"><span class="string">               mkdir $&#123;chartDir&#125;</span></span><br><span class="line"><span class="string">               &quot;&quot;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;检出项目源码&quot;</span></span><br><span class="line">            checkout([<span class="attr">$class:</span> <span class="string">&#x27;SubversionSCM&#x27;</span>, <span class="attr">additionalCredentials:</span> [], <span class="attr">excludedCommitMessages:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">excludedRegions:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">excludedRevprop:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">excludedUsers:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">filterChangelog:</span> <span class="literal">false</span>, <span class="attr">ignoreDirPropChanges:</span> <span class="literal">false</span>, <span class="attr">includedRegions:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">locations:</span> [[<span class="attr">cancelProcessOnExternalsFail:</span> <span class="literal">true</span>, <span class="attr">credentialsId:</span> <span class="string">&quot;$&#123;jenkinsSVNCredentialsId&#125;&quot;</span>, <span class="attr">depthOption:</span> <span class="string">&#x27;infinity&#x27;</span>, <span class="attr">ignoreExternalsOption:</span> <span class="literal">true</span>, <span class="attr">local:</span> <span class="string">&quot;$&#123;mavenProjects&#125;&quot;</span>, <span class="attr">remote:</span> <span class="string">&quot;$&#123;svnRemote&#125;&quot;</span>]], <span class="attr">quietOperation:</span> <span class="literal">true</span>, <span class="attr">workspaceUpdater:</span> [<span class="attr">$class:</span> <span class="string">&#x27;UpdateUpdater&#x27;</span>]])</span><br><span class="line">            echo <span class="string">&quot;检出 helm chart&quot;</span></span><br><span class="line">            checkout([<span class="attr">$class:</span> <span class="string">&#x27;SubversionSCM&#x27;</span>, <span class="attr">additionalCredentials:</span> [], <span class="attr">excludedCommitMessages:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">excludedRegions:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">excludedRevprop:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">excludedUsers:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">filterChangelog:</span> <span class="literal">false</span>, <span class="attr">ignoreDirPropChanges:</span> <span class="literal">false</span>, <span class="attr">includedRegions:</span> <span class="string">&#x27;&#x27;</span>, <span class="attr">locations:</span> [[<span class="attr">cancelProcessOnExternalsFail:</span> <span class="literal">true</span>, <span class="attr">credentialsId:</span> <span class="string">&quot;$&#123;jenkinsSVNCredentialsId&#125;&quot;</span>, <span class="attr">depthOption:</span> <span class="string">&#x27;infinity&#x27;</span>, <span class="attr">ignoreExternalsOption:</span> <span class="literal">true</span>, <span class="attr">local:</span> <span class="string">&quot;$&#123;chartDir&#125;&quot;</span>, <span class="attr">remote:</span> <span class="string">&quot;$&#123;svnHelm&#125;&quot;</span>]], <span class="attr">quietOperation:</span> <span class="literal">true</span>, <span class="attr">workspaceUpdater:</span> [<span class="attr">$class:</span> <span class="string">&#x27;UpdateUpdater&#x27;</span>]])</span><br><span class="line">        &#125;</span><br><span class="line">        stage(<span class="string">&#x27;单元测试&#x27;</span>) &#123;</span><br><span class="line">            echo <span class="string">&quot;2. 测试阶段&quot;</span>;</span><br><span class="line">            echo <span class="string">&quot;项目 SVN 地址: $&#123;svnRemote&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;maven 编译模块目标: $&#123;mvnBuildModules&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;Docker 构建目标: $&#123;dockerRegistryUrl&#125;/$&#123;image&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;Helm 模板地址: $&#123;svnHelm&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;Helm 部署目标: namespace = $&#123;namespace&#125;, name = $&#123;appName&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;Helm 删除应用：$&#123;helmDelete&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;Helm 部署应用：$&#123;helmInstall&#125;&quot;</span></span><br><span class="line">            echo <span class="string">&quot;Helm 升级应用：$&#123;helmUpgrade&#125;&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">        stage(<span class="string">&#x27;代码编译打包&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                container(<span class="string">&#x27;maven&#x27;</span>) &#123;</span><br><span class="line">                    echo <span class="string">&quot;3. 代码编译打包阶段&quot;</span></span><br><span class="line">                    sh <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                       cd $&#123;mavenProjects&#125;</span></span><br><span class="line"><span class="string">                       mvn clean install -pl $&#123;mvnBuildModules&#125; -am -Dmaven.test.skip=true</span></span><br><span class="line"><span class="string">                       &quot;&quot;&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (exc) &#123;</span><br><span class="line">                println <span class="string">&quot;构建失败 - $&#123;currentBuild.fullDisplayName&#125;&quot;</span></span><br><span class="line">                <span class="keyword">throw</span> (exc)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        stage(<span class="string">&#x27;构建 Docker 镜像&#x27;</span>) &#123;</span><br><span class="line">            withCredentials([usernamePassword(<span class="attr">credentialsId:</span> <span class="string">&#x27;DockerHub&#x27;</span>, <span class="attr">passwordVariable:</span> <span class="string">&#x27;dockerHubPassword&#x27;</span>, <span class="attr">usernameVariable:</span> <span class="string">&#x27;dockerHubUser&#x27;</span>)]) &#123;</span><br><span class="line">                container(<span class="string">&#x27;docker&#x27;</span>) &#123;</span><br><span class="line">                    echo <span class="string">&quot;4. 构建 Docker 镜像阶段&quot;</span></span><br><span class="line">                    sh <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                       cd $&#123;dockerPath&#125;</span></span><br><span class="line"><span class="string">                       cp /root/.m2/repository/com/awifi/capacity/$&#123;appName&#125;/$&#123;imageTag&#125;/*.jar ./ &amp;&amp; pwd &amp;&amp; ls</span></span><br><span class="line"><span class="string">                       docker login $&#123;dockerRegistryUrl&#125; -u $&#123;dockerHubUser&#125; -p $&#123;dockerHubPassword&#125;</span></span><br><span class="line"><span class="string">                       docker build -t $&#123;dockerRegistryUrl&#125;/$&#123;image&#125;:$&#123;imageTag&#125; .</span></span><br><span class="line"><span class="string">                       docker push $&#123;dockerRegistryUrl&#125;/$&#123;image&#125;:$&#123;imageTag&#125;</span></span><br><span class="line"><span class="string">                       &quot;&quot;&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        stage(<span class="string">&#x27;运行 Helm 阶段&#x27;</span>) &#123;</span><br><span class="line">            container(<span class="string">&#x27;helm-kubectl&#x27;</span>) &#123;</span><br><span class="line">                echo <span class="string">&quot;5. 运行 Helm 阶段&quot;</span></span><br><span class="line">                echo <span class="string">&quot;[INFO] 开始运行 Helm 命令&quot;</span></span><br><span class="line">                <span class="keyword">if</span> (<span class="string">&quot;true&quot;</span> == <span class="string">&quot;$&#123;helmDelete&#125;&quot;</span>) &#123;</span><br><span class="line">                    echo <span class="string">&quot;[INFO] Helm 删除应用...&quot;</span></span><br><span class="line">                    sh <span class="string">&quot;helm delete $&#123;appName&#125; -n $&#123;namespace&#125;&quot;</span></span><br><span class="line">                    echo <span class="string">&quot;[INFO] Helm 删除应用成功.&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (<span class="string">&quot;true&quot;</span> == <span class="string">&quot;$&#123;helmInstall&#125;&quot;</span>) &#123;</span><br><span class="line">                    echo <span class="string">&quot;[INFO] Helm 部署应用...&quot;</span></span><br><span class="line">                    sh <span class="string">&quot;helm install $&#123;appName&#125; ./$&#123;chartDir&#125; --namespace=$&#123;namespace&#125; -f $&#123;dockerPath&#125;/values.yaml --set image.repository=192.168.195.2/$&#123;image&#125;,image.tag=$&#123;imageTag&#125;,fullnameOverride=$&#123;appName&#125;,service.type=NodePort&quot;</span></span><br><span class="line">                    echo <span class="string">&quot;[INFO] Helm 部署应用成功.&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (<span class="string">&quot;true&quot;</span> == <span class="string">&quot;$&#123;helmUpgrade&#125;&quot;</span>) &#123;</span><br><span class="line">                    echo <span class="string">&quot;[INFO] Helm 升级应用...&quot;</span></span><br><span class="line">                    sh <span class="string">&quot;helm upgrade $&#123;appName&#125; ./$&#123;chartDir&#125; --namespace=$&#123;namespace&#125; -f $&#123;dockerPath&#125;/values.yaml --set image.repository=192.168.195.2/$&#123;image&#125;,image.tag=$&#123;imageTag&#125;,fullnameOverride=$&#123;appName&#125;,service.type=NodePort&quot;</span></span><br><span class="line">                    echo <span class="string">&quot;[INFO] Helm 升级应用成功&quot;</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面这段groovy脚本比较简单，我们需要注意的是volumes区域的定义，将容器中的<code>/root/.m2</code>目录挂载到<code>nfs</code>上是为了给Maven构建添加缓存的，不然每次构建的时候都需要去重新下载依赖，这样就非常慢了；挂载<code>.kube</code>目录是为了能够让<code>kubectl</code>和<code>helm</code>两个工具可以读取到 Kubernetes 集群的连接信息，不然我们是没办法访问到集群的；最后挂载<code>/var/run/docker.sock</code>文件是为了能够让我们的docker这个容器获取到Docker Daemon的信息的，因为docker这个镜像里面只有客户端的二进制文件，我们需要使用宿主机的Docker Daemon来构建镜像，当然我们也需要在运行 Slave Pod 的节点上拥有访问集群的文件，然后在每个Stage阶段使用特定需要的容器来进行任务的描述即可，所以这几个volumes都是非常重要的。</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">volumes:</span> [</span><br><span class="line">    nfsVolume(<span class="attr">mountPath:</span> <span class="string">&#x27;/root/.m2&#x27;</span>, <span class="attr">readOnly:</span> <span class="literal">false</span>, <span class="attr">serverAddress:</span> <span class="string">&#x27;192.168.200.19&#x27;</span>, <span class="attr">serverPath:</span> <span class="string">&#x27;/home/k8s-projects/k8s-nfs/m2&#x27;</span>),</span><br><span class="line">    hostPathVolume(<span class="attr">mountPath:</span> <span class="string">&#x27;/home/jenkins/.kube&#x27;</span>, <span class="attr">hostPath:</span> <span class="string">&#x27;/root/.kube&#x27;</span>),</span><br><span class="line">    hostPathVolume(<span class="attr">mountPath:</span> <span class="string">&#x27;/var/run/docker.sock&#x27;</span>, <span class="attr">hostPath:</span> <span class="string">&#x27;/var/run/docker.sock&#x27;</span>)</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>到这里为止，你现在总共有两份文件 Dockerfile 和 Jenkinsfile，把这两份文件放到你在Jenkinsfile脚本中定义的<code>dockerPath</code> 目录下，然后提交代码到远程仓库。你还需要将<code>svnHelm</code> 目录下的 <code>values.yaml</code> 也复制到 <code>dockerPath</code> 目录下， <code>values.yaml</code> 主要是配置helm安装/更新服务的配置信息。</p>
<p>切换到 Jenkins 页面点击先前定义的流水线任务。</p>
<p><img src="https://img-blog.csdnimg.cn/20200509194818496.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpdHRsZV9meGM=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<p>正常可以看到 Jenkins 中的任务构建成功了。</p>
<h2 id="8-6-如何配置自定义SpringBoot应用启动参数"><a href="#8-6-如何配置自定义SpringBoot应用启动参数" class="headerlink" title="8.6 如何配置自定义SpringBoot应用启动参数"></a>8.6 如何配置自定义SpringBoot应用启动参数</h2><p>在 “8.5 Jenkinsfile 示例” 章节中Jenkinsfile 最后一个阶段 “运行 Helm 阶段” 中，helm 安装或更新服务的命令是</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ helm upgrade <span class="variable">$&#123;appName&#125;</span> ./<span class="variable">$&#123;chartDir&#125;</span> --namespace=<span class="variable">$&#123;namespace&#125;</span> -f <span class="variable">$&#123;dockerPath&#125;</span>/values.yaml --<span class="built_in">set</span> \</span><br><span class="line">image.repository=192.168.195.2/<span class="variable">$&#123;image&#125;</span>,image.tag=<span class="variable">$&#123;imageTag&#125;</span>,fullnameOverride=<span class="variable">$&#123;appName&#125;</span>,service.type=NodePort</span><br></pre></td></tr></table></figure>

<p>主要关注点是 <code>values.yaml</code> : 你可以修改 <code>container.args</code> 和 <code>container.additionalArgs</code> 来改变Java应用的启动命令参数。</p>
<p>下面是helm中关于容器运行启动的命令配置部分。</p>
<p><code>range</code> 表示循环语句， <code>container.args</code>  对应下面的<code>.Values.container.args</code> ，<code>container.additionalArgs</code> 对应下面的<code>.Values.container.additionalArgs</code></p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">...</span></span><br><span class="line">     <span class="attr">containers:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> &#123;&#123; <span class="string">include</span> <span class="string">&quot;helm-spring-cloud.fullname&quot;</span> <span class="string">.</span> &#125;&#125;</span><br><span class="line">          <span class="attr">securityContext:</span></span><br><span class="line">            &#123;&#123;<span class="bullet">-</span> <span class="string">toYaml</span> <span class="string">.Values.securityContext</span> <span class="string">|</span> <span class="string">nindent</span> <span class="number">12</span> &#125;&#125;</span><br><span class="line">          <span class="attr">image:</span> <span class="string">&quot;<span class="template-variable">&#123;&#123; .Values.image.repository &#125;&#125;</span>:<span class="template-variable">&#123;&#123; .Values.image.tag &#125;&#125;</span>&quot;</span></span><br><span class="line">          <span class="attr">imagePullPolicy:</span> &#123;&#123; <span class="string">.Values.image.pullPolicy</span> &#125;&#125;</span><br><span class="line">          <span class="attr">command:</span> [<span class="string">&quot;java&quot;</span>]</span><br><span class="line">          <span class="attr">args:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">-Duser.timezone=GMT+8</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">-Djava.security.egd=file:/dev/./urandom</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">-Dserver.port=8080</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">-Dspring.cloud.consul.discovery.prefer-ip-address=false</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">-Dspring.cloud.consul.discovery.hostname=&#123;&#123;-</span> <span class="string">include</span> <span class="string">&quot;helm-spring-cloud.fullname&quot;</span> <span class="string">.&#125;&#125;</span></span><br><span class="line">            &#123;&#123;<span class="bullet">-</span> <span class="string">range</span> <span class="string">.Values.container.args</span> &#125;&#125;</span><br><span class="line">            <span class="bullet">-</span> &#123;&#123; <span class="string">.</span> <span class="string">|</span> <span class="string">quote</span> &#125;&#125;</span><br><span class="line">            &#123;&#123;<span class="bullet">-</span> <span class="string">end</span> &#125;&#125;</span><br><span class="line">            <span class="bullet">-</span> <span class="string">-jar</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">/app/app.jar</span></span><br><span class="line">            &#123;&#123;<span class="bullet">-</span> <span class="string">range</span> <span class="string">.Values.container.additionalArgs</span> &#125;&#125;</span><br><span class="line">            <span class="bullet">-</span> &#123;&#123; <span class="string">.</span> <span class="string">|</span> <span class="string">quote</span> &#125;&#125;</span><br><span class="line">            &#123;&#123;<span class="bullet">-</span> <span class="string">end</span> &#125;&#125;</span><br><span class="line"><span class="string">...</span></span><br></pre></td></tr></table></figure>

<h1 id="参考资源"><a href="#参考资源" class="headerlink" title="参考资源"></a>参考资源</h1><p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/xulingjie/p/9916345.html">jenkins+svn+pipeline+kubernetes部署java应用（一）</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/xulingjie/p/9916768.html">jenkins+svn+pipeline+kubernetes部署java应用（二）</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/xulingjie/p/9916904.html">jenkins+svn+pipeline+kubernetes部署java应用（三）</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/diantun00/article/details/81075007">玩转Jenkins Pipeline_运维_大宝鱼的博客-CSDN博客</a></p>
<p><a target="_blank" rel="noopener" href="https://www.qikqiak.com/post/kubernetes-jenkins1/">基于 kubernetes 的动态 jenkins slave</a></p>
<p><a target="_blank" rel="noopener" href="https://www.qikqiak.com/post/kubernetes-jenkins2/">Jenkins Pipeline 部署 Kubernetes 应用(二)</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/hiver/p/7850954.html">Maven的-pl -am -amd参数学习 - hiv - 博客园</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lxwphp/p/9109685.html">SVN常用命令之checkout</a></p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/k8s/" rel="tag"># k8s</a>
              <a href="/tags/helm/" rel="tag"># helm</a>
              <a href="/tags/jenkins/" rel="tag"># jenkins</a>
              <a href="/tags/CI-CD/" rel="tag"># CI/CD</a>
              <a href="/tags/svn/" rel="tag"># svn</a>
              <a href="/tags/devops/" rel="tag"># devops</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2020/04/28/Kubernetes%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96/" rel="prev" title="Kubernetes数据持久化">
                  <i class="fa fa-chevron-left"></i> Kubernetes数据持久化
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2020/04/29/CentOS7%E4%B8%8Byum%E5%AE%89%E8%A3%85%E5%92%8C%E9%85%8D%E7%BD%AENFS/" rel="next" title="CentOS7下yum安装和配置NFS">
                  CentOS7下yum安装和配置NFS <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>







<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>
</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">一年春又来</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/mist/" class="theme-link" rel="noopener" target="_blank">NexT.Mist</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  
<script src="/js/local-search.js"></script>






  





</body>
</html>
